package advanced;

import JaCoP.constraints.Diff2;
import JaCoP.constraints.Max;
import JaCoP.constraints.Sum;
import JaCoP.constraints.XgteqY;
import JaCoP.core.IntVar;
import JaCoP.core.Store;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleSelect;
import JaCoP.search.SmallestMax;

public class Jobshop {
	private static Store store = new Store();

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		long T1, T2, T;
		T1 = System.currentTimeMillis();
		scheduleJobs();
		T2 = System.currentTimeMillis();
		T = T2 - T1;
		System.out.println("\n\t*** Execution time = " + T + " ms");
	}

	private static void scheduleJobs() {

		IntVar[][][] tasks = new IntVar[6][6][2]; // jobs, tasks, [machine, id]
		int[] durations = { 1, 3, 6, 7, 3, 6, 8, 5, 10, 10, 10, 4, 5, 4, 8, 9,
				1, 7, 5, 5, 5, 3, 8, 9, 9, 3, 5, 4, 3, 1, 3, 3, 9, 10, 4, 1 };
		int sumDurations = 0;
		for (int i : durations) {
			sumDurations += i;
		}
		int[] machineIDs = { 2, 0, 1, 3, 5, 4, 1, 2, 4, 5, 0, 3, 2, 3, 5, 0, 1,
				4, 1, 0, 2, 3, 4, 5, 2, 1, 4, 5, 0, 3, 1, 3, 5, 0, 4, 2 };
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				tasks[job][task][0] = new IntVar(store, "J" + job + "T" + task
						+ "M" + machineIDs[job * 6 + task], machineIDs[job * 6
						+ task], machineIDs[job * 6 + task]);
				tasks[job][task][1] = new IntVar(store, "J" + job + "T" + task
						+ "D" + durations[job * 6 + task], durations[job * 6
						+ task], durations[job * 6 + task]);
			}
		}

		IntVar[] jobTimeOrigins = new IntVar[6 * 6];
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				jobTimeOrigins[job * 6 + task] = new IntVar(store, "OriJ" + job
						+ "T" + task, 0, sumDurations);
			}
		}
		IntVar[] jobTimeEndings = new IntVar[6 * 6];
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				jobTimeEndings[job * 6 + task] = new IntVar(store, "EndJ" + job
						+ "T" + task, 0, sumDurations);
				store.impose(new Sum(new IntVar[] {
						jobTimeOrigins[job * 6 + task], tasks[job][task][1] },
						jobTimeEndings[job * 6 + task]));
			}
		}
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 5; task++) {
				store.impose(new XgteqY(jobTimeOrigins[job * 6 + task + 1],
						jobTimeEndings[job * 6 + task]));
			}
		}
		IntVar[][] rectangles = new IntVar[6 * 6][4];
		IntVar one = new IntVar(store, "one", 1, 1);
		for (int job = 0; job < 6; job++) {
			for (int task = 0; task < 6; task++) {
				rectangles[job * 6 + task] = new IntVar[] {
						jobTimeOrigins[job * 6 + task], tasks[job][task][0],
						tasks[job][task][1], one }; // O1,O2,L1,L2 = OTime,
													// OMachine, LTime, LMachine
													// = 1
			}
		}
		store.impose(new Diff2(rectangles));

		IntVar maxEnding = new IntVar(store, "MaxEnd", 0, sumDurations);
		store.impose(new Max(jobTimeEndings, maxEnding));

		// Origins x-wise have to be large domains, 0..sumDurations
		// Then for each job we put a constraint to prevent task 2 to be
		// started before task 1 is completed
		// Durations are locked
		// Origins y-wise have to be locked to a certain machine
		// Minimize the length of the x axis

		Search<IntVar> label = new DepthFirstSearch<IntVar>();

		SelectChoicePoint<IntVar> select = new SimpleSelect<IntVar>(
				jobTimeOrigins, new SmallestMax<IntVar>(), new IndomainMin<IntVar>());

		boolean Result = label.labeling(store, select, maxEnding);

		if (Result) {
			System.out.println("Minimum time for schedule is " + maxEnding);
		} else {
			System.out.println("\n*** No");
		}

	}

}
